﻿using DeHeng_Mes.Dao.PlcDao;
using DeHeng_Mes.Dao;
using DeHeng_Mes.Model.Singleton;
using DeHeng_Mes.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DeHeng_Mes.Model.ConfigModel;
using DeHeng_Mes.Utils;
using DeHeng_Mes.Model.Table;
using S7.Net;

namespace DeHeng_Mes.Thread
{

    // 定义一个委托，代表获取错误类型和错误信息的方法
    public delegate (string errorType, string errorMessage) GetErrorMessageDelegate(int byteIndex, int bitIndex);
    public class ErrorGather
    {
        PlcDao plcDao = new PlcDao();

        List<ErrorCache> lists = ErrorList.Lists;

        DeviceDao deviceDao = new DeviceDao();

        ErrorDao errorDao = new ErrorDao();

        /// <summary>
        /// 采集报警
        /// </summary>
        /// <returns></returns>
        public async Task GetError()
        {
            Tools.ProduceLogText("报警采集线程启动成功！");
            while (true)
            {
                try
                {
                    List<PlcOrderConfigModel> readPlcList = PlcOrderConfigModelList.ReadPlcList.Where(x => x.plc.IsConnected == true).ToList();

                    foreach (var item in readPlcList)
                    {
                        switch (item.PlcName)
                        {
                            case "P1":
                                HandlePlcAlarm(item.PlcName, item.plc,100,2,22,MappingTable.GetP1ErrorMessage);
                                break;
                            case "P2":
                                HandlePlcAlarm(item.PlcName, item.plc,204,2,102, MappingTable.GetP2ErrorMessage);
                                break;
                            case "P3":
                                HandlePlcAlarm(item.PlcName, item.plc,242,2,102, MappingTable.GetP3ErrorMessage);
                                break;
                            case "P4":
                                HandlePlcAlarm(item.PlcName, item.plc,242,2,102, MappingTable.GetP4ErrorMessage);
                                break;

                            default:
                                break;

                        }

                    }

                }
                catch (Exception ex)
                {
                    Tools.ProduceLogText($"采集报警信息失败，原因为{ex.Message}");
                }
                await Task.Delay(1000);
            }
        }

        /// <summary>
        /// 判断报警是否存在于缓存数据中
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private bool IsExist(string message)
        {
            return ErrorDic.GetAllAlarms().Values.Contains(message);
        }


        private void HandlePlcAlarm(string plcName, Plc plc,int dbEND,int elseStartDB,int elseEndDB, GetErrorMessageDelegate getErrorMessage)
        {
            //读取报警信息
            byte[] bytes = plc.ReadBytes(DataType.DataBlock, 5110, 0, dbEND);

            for (int i = 0; i < bytes.Length; i++)
            {
                //处理int报警
                if (i >= elseStartDB && i <= elseEndDB)
                {
                    if (i % 2 == 0)
                    {
                        //拿到报警状态
                        int status = S7.Net.Types.Int.FromByteArray(bytes.Skip(i).Take(2).ToArray());
                        //那拿到报警类型和报警信息
                        (string errorType, string errorMessage) = getErrorMessage(i, 0);

                        // 存在报警,先获取对应的报警文本
                        if (string.IsNullOrEmpty(errorMessage))
                            continue;

                        // 1.到全局缓存中查找有报警信息
                        bool isExit = IsExist(errorMessage);

                        if (status == 1 && !isExit)
                        {
                            // 2.缓存有报警信息就跳过，没有报警信息存储数据库记录开始时间并且全局缓存报警，记录缓存时候带上数据库主键
                            DeviceFault deviceFault = new DeviceFault()
                            {

                                PlcName = plcName,

                                FaultType = errorType,

                                FaultMessage = errorMessage,

                                StopStartTime = DateTime.Now,
                            };
                            //插入数据库返回id
                            int id = errorDao.insertError(deviceFault);
                            //将报警存储在缓存中
                            ErrorDic.AddAlarm(id, errorMessage);

                            Tools.ProduceLogText($"添加了一条报警信息到缓存中，报警信息为{deviceFault.FaultMessage}");
                        }
                        else if (status == 0 && isExit)
                        {
                            // 1.到全局缓存找有没有当前报警缓存，有就根据主键更新数据库中结束时间并且清除当前一条缓存，没有就跳过
                            int id = ErrorDic.GetAlarmIdByMessage(errorMessage);

                            if (id != 0)
                            {
                                // 更新数据库结束时间
                                errorDao.updateError(DateTime.Now, id);

                                // 从缓存移除
                                ErrorDic.RemoveAlarm(id);

                                Tools.ProduceLogText($"从缓存中移除了一条报警信息，报警缓存id为{id}，报警信息为{errorMessage}");
                            }
                        }
                    }
                }
                else
                {
                    //处理bool类型报警
                    for (int j = 0; j < 8; j++)
                    {
                        bool isError = S7.Net.Types.Bit.FromByte(bytes[i], (byte)j);

                        (string errorType, string errorMessage) = getErrorMessage(i, j);

                        bool isExit = IsExist(errorMessage);

                        if (isError && !isExit)
                        {
                            // 存在报警,先获取对应的报警文本
                            if (string.IsNullOrEmpty(errorMessage))
                                continue;

                            // 2.缓存有报警信息就跳过，没有报警信息存储数据库记录开始时间并且全局缓存报警，记录缓存时候带上数据库主键
                            DeviceFault deviceFault = new DeviceFault()
                            {

                                PlcName = plcName,

                                FaultType = errorType,

                                FaultMessage = errorMessage,

                                StopStartTime = DateTime.Now,
                            };

                            int id = errorDao.insertError(deviceFault);

                            ErrorDic.AddAlarm(id, errorMessage);

                            Tools.ProduceLogText($"添加了一条报警信息到缓存中，报警信息为{deviceFault.FaultMessage}");
                        }
                        else if (!isError && isExit)
                        {
                            // 1.到全局缓存找有没有当前报警缓存，有就根据主键更新数据库中结束时间并且清除当前一条缓存，没有就跳过
                            int id = ErrorDic.GetAlarmIdByMessage(errorMessage);

                            if (id != 0)
                            {
                                // 更新数据库结束时间
                                errorDao.updateError(DateTime.Now, id);

                                // 从缓存移除
                                ErrorDic.RemoveAlarm(id);

                                Tools.ProduceLogText($"从缓存中移除了一条报警信息，报警缓存id为{id}，报警信息为{errorMessage}");
                            }
                        }
                    }
                }
            }

        }

    }
}
